using System;
using System.Collections;
using System.Text;

using Mono.Cecil;
using Mono.Cecil.Cil;

namespace CILUtil
{
    public class FormatEntity
    {
        private static string Join(params string[] strs)
        {
            return string.Join("#", strs);
        }

        public static string Encode( ParameterDefinition param )
        {
            return Join("ParameterDefinition",
                        Encode(param.Method),
                        param.Name,
                        Encode(param.ParameterType));
        }
        
        public static string Encode(ParameterReference param)
        {
            return Join("ParameterReference",
                        param.Name, 
                        Encode(param.ParameterType));
        }

        public static string Encode( VariableDefinition var )
        {
            return Join("VariableDefinition",
                        Encode(var.Method),
                        var.Name,
                        Encode(var.VariableType));
        }
        public static string Encode(VariableReference var)
        {
            return Join("VariableReference",
                        var.Name,
                        Encode(var.VariableType));
        }

        public static string Encode(FieldDefinition field)
        {
            return Join("FieldDefinition",
                        Encode(field.DeclaringType),
                        field.Name,
                        Encode(field.FieldType));
        }
        public static string Encode(FieldReference field)
        {
            return Join("FieldReference",
                        Encode(field.DeclaringType),
                        field.Name,
                        Encode(field.FieldType));
        }
        public static string Encode(PropertyDefinition prop)
        {
            return Join("PropertyDefinition",
                        Encode(prop.DeclaringType),
                        prop.Name,
                        Encode(prop.PropertyType));
        }
        public static string Encode(PropertyReference prop)
        {
            return Join("PropertyReference",
                        Encode(prop.DeclaringType),
                        prop.Name,
                        Encode(prop.PropertyType));
        }
        public static string Encode(MethodDefinition meth)
        {
            return Join("MethodDefinition",
                        Encode(meth.DeclaringType),
                        CodeProperties.GetMethodNameWithParametersTight(meth, true));
        }
        public static string Encode(MethodReference meth)
        {
            return Join("MethodReference",
                        Encode(meth.DeclaringType),
                        CodeProperties.GetMethodNameWithParametersTight(meth, true));
        }
        public static string Encode(TypeDefinition type)
        {
            return Join("TypeDefinition",
                        CodeProperties.GetClassName(type));
        }
        public static string Encode(TypeReference type)
        {
            return Join("TypeReference",
                        CodeProperties.GetClassName(type));
        }
        public static string Encode(FunctionPointerType func)
        {
            return Join("FunctionPointerType",
                        Encode(func.DeclaringType),
                        func.FullName);
        }
        public static string Encode(GenericParameter genparam)
        {
            return Join("GenericParameter",
                        Encode(genparam.DeclaringType),
                        genparam.FullName);
        }
        public static string Encode(string str)
        {
            return Join("String",
                        System.Text.RegularExpressions.Regex.Escape( str ));
        }
        public static string Encode(int intToStr)
        {
            return Join("Integer",
                        intToStr.ToString());
        }

        public static string[] Encode(ArrayList objects )
        {
            string[] strObjs = new string[objects.Count];
            int i = 0;
            foreach (object obj in objects)
            {
                strObjs[i] = EncodeObject(obj);
                i++;
            }
            return strObjs;
        }

        private static string EncodeObject(object subject)
        {
            if (subject is ParameterDefinition)
            {
                ParameterDefinition param = (ParameterDefinition)subject;
                return Encode(param);
            }
            if (subject is ParameterReference)
            {
                ParameterReference param = (ParameterReference)subject;
                return Encode(param);
            }
            if (subject is VariableDefinition)
            {
                VariableDefinition var = (VariableDefinition)subject;
                return Encode(var);
            }
            if (subject is VariableReference)
            {
                VariableReference var = (VariableReference)subject;
                return Encode(var);
            }
            if (subject is FieldDefinition)
            {
                FieldDefinition field = (FieldDefinition)subject;
                return Encode(field); //static,public,etc...
            }
            if (subject is FieldReference)
            {
                FieldReference field = (FieldReference)subject;
                return Encode(field); //static,public,etc...
            }
            if (subject is PropertyDefinition)
            {
                PropertyDefinition prop = (PropertyDefinition)subject;
                return Encode(prop);
            }
            if (subject is PropertyReference)
            {
                PropertyReference prop = (PropertyReference)subject;
                return Encode(prop);
            }
            if (subject is MethodDefinition)
            {
                MethodDefinition meth = (MethodDefinition)subject;
                return Encode(meth);
            }
            if (subject is MethodReference)
            {
                MethodReference meth = (MethodReference)subject;
                return Encode(meth);
            }
            if (subject is TypeDefinition)
            {
                TypeDefinition type = (TypeDefinition)subject;
                return Encode(type);
            }
            if (subject is TypeReference)
            {
                TypeReference type = (TypeReference)subject;
                return Encode(type);
            }
            //if (subject is EventDefinition)
            //{
            //    EventDefinition eventDef = (EventDefinition)subject;
            //    return Encode(eventDef);
            //}
            //if (subject is EventReference)
            //{
            //    EventReference eventRef = (EventReference)subject;
            //    return Encode(eventRef);
            //}
            if (subject is FunctionPointerType)
            {
                FunctionPointerType func = (FunctionPointerType)subject;
                return Encode(func);
            }
            if (subject is GenericParameter)
            {
                GenericParameter genparam = (GenericParameter)subject;
                return Encode(genparam);
            }
            if (subject is String)
            {
                String str = (String)subject;
                return Encode(str);
            }
            if (subject is Int32)
            {
                int i = (int)subject;
                return Encode(i);
            }
            return null;
        }

        // Someone invent double dispatch...
        public static string Encode(MethodDefinition md, Instruction i)
        {
            object subject = i.Operand == null ? CILUtil.Entity.GetOperand(md, i) : i.Operand;
            return EncodeObject(subject);
        }
    }
}
